822. 翻转卡片游戏
822. 翻转卡片游戏
Similar Question
Solution Tips
方案一: 哈希表 + 模拟
var flipgame = function(fronts, backs) {
// 有点像动态规划, 但是情况讨论好复杂啊
// 正面和背面是可以相互转换的, 只是代表着当前状态
// 任意一张卡片数字, 都可以和其他所有的卡片的任意正反面组合
// 唯一不能组合的就是自己的正反面
// 如果要选第 0 张的话, 不可能, 因为正面与背面的数相同, 无论何时都会存在 fronts 含有与 backs[0] 相同
// 所以, 所有的正反相同的卡片都不可能符合题意
// 选第 1 张, 就是示例的情况, 如果不反转, 3 也是候选答案之一
// 需要两个哈希表, 一个存储住正面, 一个存储反面
let frontsMap = {};
let backsMap = {};
// init maps
for (let i = 0; i < fronts.length; i++) {
addNumber(fronts[i], backs[i]);
}
let res = Number.MAX_SAFE_INTEGER;
for (let i = 0; i < fronts.length; i++) {
if (fronts[i] === backs[i]) {
continue;
}
// 0 or undefined, 背面的数字, 在正面没有
if (!frontsMap[backs[i]]) {
// 符合题意
res = Math.min(res, backs[i]);
}
// 考虑反转当前的卡片
// 因为这张卡片正面和反面都不相同, 而且反面的数字在正面没有
// 如果正面的数字 hashMap 为 1 的话, 那就是仅有自己
// 这个时候直接进行一次反转, 也是符合题意的答案
// 没办法在一个 for 循环里把其他相同的卡片都反转
// 反转所有的 fonts[i]
const tempFrontsMap = { ...frontsMap };
const tempBacksMap = { ...backsMap };
let sameSide = false;
for (let j = 0; j < fronts.length; j++) {
// 如果有一张卡片正反面都与 fronts[i] 相同
// 那么无论如何反转都不会符合题意直接退出
if (fronts[j] === backs[j] && fronts[j] === fronts[i]) {
sameSide = true;
break;
}
if (fronts[j] === fronts[i]) {
// 反转
// [fronts[j], backs[j]] = [backs[j], fronts[j]]
// 更新 map
frontsMap[fronts[j]]--;
backsMap[backs[j]]--;
addNumber(backs[j], fronts[j]);
}
}
// 所有的与正面相同的卡片都反转到背面了, 检查是否符合题意
if (sameSide) {
// 还原 Map
frontsMap = tempFrontsMap;
backsMap = tempBacksMap;
continue;
};
if (!frontsMap[fronts[i]]) {
res = Math.min(res, fronts[i]);
}
// 还原 Map
frontsMap = tempFrontsMap;
backsMap = tempBacksMap;
}
return res === Number.MAX_SAFE_INTEGER ? 0 : res;
function addNumber(front, back) {
if (frontsMap[front] === undefined) {
frontsMap[front] = 1;
}
else {
frontsMap[front]++;
}
if (backsMap[back] === undefined) {
backsMap[back] = 1;
}
else {
backsMap[back]++;
}
}
};
// let fronts = [1,2,4,4,7], backs = [1,3,4,1,3]
// let fronts = [1], backs = [1]
let fronts = [1,1,1,3,2,2,2,1,2,3]
let backs = [2,3,1,2,1,2,2,2,2,3]
console.log(flipgame(fronts, backs))
每一次考虑正面的时候, 反转所有相同的卡牌, 其实会有重复的工作. 这里可以用贪心算法小优化一下, 如有正反相同的卡牌, 后续遇到正反可以不用判断: banMap
考虑动态规划呢?
定义 dp[i]
为前 i 张卡片, 符合题意的值
只有正反小于 dp[i - 1]
才有反转的价值, 那还是得反转, 只能小优化一下
Map 的还原能否优化一下?
方案二: 脑筋急转弯 + 思路清晰版
如果一张卡片正反两面有相同的数字,那么这张卡片无论怎么翻转,正面都是这个数字,这个数字即不能是最后所选的数字 x。
按照这个思路,我们首先遍历所有卡片,如果卡片上的两个数字相同,则加入哈希集合 same 中,除此集合外的所有数字,都可以被选做 x, 我们只需要再次遍历所有数字,找到最小值即可。
最后,我们返回找到的最小值,如果没有则返回 0。
var flipgame = function(fronts, backs) {
const same = new Set();
for (let i = 0; i < fronts.length; i++) {
if (fronts[i] === backs[i]) {
same.add(fronts[i]);
}
}
let res = 3000;
for (let x of fronts) {
if (x < res && !same.has(x)) {
res = x;
}
}
for (let x of backs) {
if (x < res && !same.has(x)) {
res = x;
}
}
return res % 3000;
};